# @Time : 2020/10/13 10:39
# @Author : Fioman
# @Phone : 13149920693
"""
算法更新,使用单拼查找算法.如果找不到的情况,就用外界根据留边宽度去查找.反正最后一定要找到板.如果找到的物料框的面积小于填的尺寸.
这个时候,要根据留边宽度去弄.
1> 参数化,使用阈值,高斯滤波的内核,腐蚀膨胀的次数,高斯滤波的核大小.
2> 如果找不到,怎么办
3> 怎么判定找到的边是否满足要求呢
4> 首先是自检,填入的尺寸是否满足要求.
5> 找到的框,如果面积过大,这个时候也要将识别的结果找出来,只是会提示它尺寸上有错误.如果都找不到,就用第一次的结果返回.或者误差最小的结果返回.
"""
import os
import time
import cv2 as cv
import imutils
import numpy as np
from settings import *


def image_show(name, image, time, showState=False):
    """
    显示图像,调试的时候使用,根据一定的比率进行缩放.调试的时候可以将if False改成 if True
    :param name:
    :param image:
    :param time:
    :param showState:
    :return:
    """
    if True:
        rate = 6
        width, height = int(image.shape[1] / rate), int(image.shape[0] / rate)
        showImage = cv.resize(image, (width, height), interpolation=cv.INTER_AREA)
        cv.namedWindow(name, cv.WINDOW_AUTOSIZE)
        cv.imshow(name, showImage)
        cv.waitKey(0)


def get_width_and_height(fileName):
    """
    根据文件名获取宽高,主要是用来测试算法的时候使用,文件名必须是固定的格式,以.bmp结尾,并且包括w_和h_并且w_和h_后面跟着的必须是4位数.
    :param fileName: 文件名,类似188--2020-09-21_16-45-46--w_1989--h_1745.bmp 这种图像文件
    :return: 提取出来w:1989, h:1745  单位像素
    """
    if fileName[-4:] != ".bmp" or "w_" not in fileName or "h_" not in fileName:
        return None, None

    widthIndex = fileName.index("w_")
    heightIndex = fileName.index("h_")
    width = fileName[widthIndex + 2:widthIndex + 6]
    height = fileName[heightIndex + 2:heightIndex + 6]
    return int(width), int(height)


def find_total_board(image, width, height, totalBoardThresUsed, filterAreaRate):
    """
    根据width和height,和分割阈值,还有就是过滤筛选条件找到物料区域
    :param image: 原始图像,整个图像,包括物料区域和台布
    :param width: 物料的宽
    :param height: 物料的高
    :param totalBoardThresUsed:使用的分割查找阈值
    :param filterAreaRate: 只是用的面积过滤值
    :return: 返回处理过的整个图,还有物料区域的box的位置信息
    """
    # 1. 先对原图进行阈值分割
    T, board = cv.threshold(image, totalBoardThresUsed, 255, cv.THRESH_BINARY)
    image_show("FindAllBoardThres", board, 0, True)
    cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)  # 防止opencv的改变,从而改变了findContours的返回值

    # 2. 然后连接所有找到的满足要求的区域,通过白线连接起来,形成整个物料框
    singleBoardArea = width * height
    largeContours = [cnt for cnt in cnts if cv.contourArea(cnt) > filterAreaRate * singleBoardArea]
    if len(largeContours) >= 2:
        centerPoints = [(int(cv.minAreaRect(contour)[0][0]), int(cv.minAreaRect(contour)[0][1])) for contour in largeContours]
        for index, center in enumerate(centerPoints):
            anotherCenter = centerPoints[(index + 1) % len(centerPoints)]
            cv.line(board, center, anotherCenter, 255, 20)
        image_show("JointCenterLine", board, 0, True)
        # 3. 重新再找一遍轮廓,防止漏掉断板的物料区域
        cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
        cnts = imutils.grab_contours(cnts)
        cntFinalFinded = sorted(cnts, key=cv.contourArea, reverse=True)[0]
    else:
        cntFinalFinded = largeContours[0]

    box = cv.minAreaRect(cntFinalFinded)
    (cx, cy), (w, h), angle = box
    if angle > 70:
        angle = 90 - angle
        box = (cx, cy), (h, w), angle
    elif angle < -70:
        angle = 90 + angle
        box = (cx, cy), (h, w), angle

    rectPoints = np.int0(cv.boxPoints(box))
    boxMask = np.zeros(image.shape, dtype=np.uint8)
    cv.drawContours(boxMask, [rectPoints], -1, 255, -1)
    imageBlackAround = cv.bitwise_and(image, boxMask)
    return imageBlackAround, box


def image_destroy():
    """
    移除掉所有的显示窗口
    :return:
    """
    cv.destroyAllWindows()


def get_rect_points_clockwise(boxPoints):
    """
    将一个矩形的四个顶点,按照从左上角开始的顺时针方向调整之后的顺序返回
    :param boxPoints: 原始的box的矩形的四个顶点的坐标信息,无序的
    :return: 返回有序的四个顶点坐标
    """
    # 1.先进行坐标之和的排序,左上角和右下角先确定.左上角最小,右下角最大
    rectPointsSorted = sorted(boxPoints, key=np.sum)
    leftTop = rectPointsSorted[0]
    rightBottom = rectPointsSorted[-1]

    # 2. 然后根据另外两个框,对比x的值.x比较大的是rightTop
    if rectPointsSorted[1][0] > rectPointsSorted[2][0]:
        rightTop = rectPointsSorted[1]
        leftBottom = rectPointsSorted[2]
    else:
        rightTop = rectPointsSorted[2]
        leftBottom = rectPointsSorted[1]
    return leftTop, rightTop, rightBottom, leftBottom


def get_two_points_middle(p1, p2):
    """
    获取两个点的中点
    :param p1: 点1
    :param p2: 点2
    :return: 返回两个点的中点坐标
    """
    assert len(p1) == 2
    assert len(p2) == 2
    middleX = (p1[0] + p2[0]) / 2
    middleY = (p1[1] + p2[1]) / 2
    return round(middleX, 2), round(middleY, 2)


def distance(p1, p2):
    """
    计算平面上两个点之间的欧式几何距离
    :param p1: 点1
    :param p2: 点2
    :return: 返回两个点之间的距离
    """
    assert len(p1) == 2
    assert len(p2) == 2
    dis = np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)
    return round(dis, 3)


def check_board_size(cnt, width, height, calcThresBig, calcThresSmall):
    """
    对找到的内板进行尺寸验证,如果满足条件返回"ok",不满足返回尺寸错误,或者是直接是叠板情况
    :param cnt:
    :param width:
    :param height:
    :param calcThresBig:
    :param calcThresSmall:
    :return:
    """
    res = {"result": "fail", "box": None, "info": "", "calcSize": None, "calcError": None}
    try:
        widthMax = width + calcThresBig * MM2PIX
        heightMax = height + calcThresBig * MM2PIX
        widthMin = width - calcThresSmall * MM2PIX
        heightMin = height - calcThresSmall * MM2PIX

        box = cv.minAreaRect(cnt)
        rectPoints = np.int0(cv.boxPoints(box))

        # 获取四个顶点坐标,从左上角顺时针方向排列
        leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(rectPoints)
        widthFinded = distance(leftTop, rightTop)
        heightFinded = distance(leftTop, leftBottom)
        res["calcSize"] = (widthFinded // MM2Pix, heightFinded // MM2Pix)
        res["calcError"] = (abs(widthFinded - width) // MM2Pix, abs(heightFinded - height) // MM2Pix)
        if widthFinded < widthMin:
            res["result"] = "small"
            res["info"] = "宽度识别过小,识别宽度:{:.2f},工单宽度:{:.2f},阈值:{:.2f}".format(widthFinded // MM2PIX, width // MM2PIX, widthMin // MM2PIX)
            return res
        elif widthFinded > widthMax:
            res["result"] = "big"
            res["info"] = "宽度识别过大,识别宽度:{:.2f},工单宽度:{:.2f},阈值:{:.2f}".format(widthFinded // MM2PIX, width // MM2PIX, widthMax // MM2PIX)
            return res
        elif heightFinded < heightMin:
            res["result"] = "small"
            res["info"] = "高度识别过小,识别高度:{:.2f},工单高度:{:.2f},阈值:{:.2f}".format(heightFinded // MM2PIX, height // MM2PIX, heightMin // MM2PIX)
            return res
        elif heightFinded > heightMax:
            # 判断是否找到了图像的边界如果是,就证明是没有中缝的情况,这个时候就要通过画椭圆的方式搞定.
            res["result"] = "big"
            res["info"] = "高度识别过大,识别高度:{:.2f},工单高度:{:.2f},阈值:{:.2f}".format(heightFinded // MM2PIX, height // MM2PIX, heightMax // MM2PIX)
            return res
        else:
            res["result"] = "ok"
            res["box"] = [leftTop, rightTop, rightBottom, leftBottom]
            res["info"] = "识别成功"
            return res
    except Exception as e:
        res["result"] = "error"
        res["info"] = str(e)
        return res


def check_inner_board(imgMoveAngle, width, height, calcThresBig, calcThresSmall, boardColor, boardPos):
    """
    检查找到的内板,根据去角之后的图像.
    :param imgMoveAngle: 最后处理掉的包含内板的二值图像
    :param width: 内板宽
    :param height: 内板高
    :param calcThresBig:识别偏大阈值
    :param calcThresSmall: 识别偏小阈值
    :param boardColor: 物料区域的彩色图像
    :param boardPos: 板的位置
    :return: 返回res,包含集中情况,需要分别去考虑.
    """
    useImg = boardColor.copy()
    boardArea = width * height
    res = {"result": "fail", "box": None, "info": "", "calcSize": None, "calcError": None}
    cnts = cv.findContours(imgMoveAngle.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
    area = 0
    w = 0
    h = 0
    try:
        _, (w, h), _ = cv.minAreaRect(cntsSorted[0])
        area = w * h
        if len(cnts) == 0:
            res["result"] = "fail"
            res["info"] = "找内板失败,没有找到任何轮廓!"
            return res
        elif 1.3 * boardArea > area > 0.8 * boardArea:
            rectBox = cv.minAreaRect(cntsSorted[0])
            rectBoxPoints = np.int0(cv.boxPoints(rectBox))
            cv.drawContours(useImg, [rectBoxPoints], -1, (0, 255, 0), 3)
            image_show("InnerBoardFinded", useImg, 0, True)
            leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(rectBoxPoints)

            if abs(get_two_points_middle(leftBottom, rightBottom)[1] - imgMoveAngle.shape[0]) < 10 * MM2PIX and boardPos == "up" and abs(
                    get_two_points_middle(leftTop, rightTop)[1]) >= 10 * MM2PIX:
                # 上框没有中缝的情况
                topMid = np.add(leftTop, rightTop) / 2
                topX = topMid[0]
                topY = topMid[1] + height
                ellipseR = int(height * 0.3)
                ellipseLong = int(width * 1.1)
                center = (int(topX), int(topY - ellipseR))
                ellipseImg = imgMoveAngle.copy()
                cv.ellipse(ellipseImg, center, (ellipseLong, ellipseR), 0, 0, 180, 0, 3)
                image_show("Ellipse", ellipseImg, 0, True)

                # 再找一次轮廓
                cnts = cv.findContours(ellipseImg, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
                cnts = imutils.grab_contours(cnts)
                cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
                rectBox = cv.minAreaRect(cntsSorted[0])
                rectBoxPoints = np.int0(cv.boxPoints(rectBox))
                useImg = boardColor.copy()
                cv.drawContours(useImg, [rectBoxPoints], -1, (0, 255, 0), 3)
                image_show("InnerBoardFinded", useImg, 0, True)

            if abs(get_two_points_middle(leftTop, rightTop)[1]) < 10 * MM2PIX and boardPos == "down" and abs(
                    get_two_points_middle(leftBottom, rightBottom)[1] - imgMoveAngle.shape[0]) > 10 * MM2PIX:  # 下框没有中缝的情况
                bottomMid = np.add(leftBottom, rightBottom) / 2
                bottomX = bottomMid[0]
                bottomY = bottomMid[1] - height
                ellipseR = int(height * 0.3)
                ellipseLong = int(width * 1.1)
                center = (int(bottomX), int(bottomY + ellipseR))
                ellipseImg = imgMoveAngle.copy()
                cv.ellipse(ellipseImg, center, (ellipseLong, ellipseR), 0, 180, 360, 0, 3)
                image_show("Ellipse", ellipseImg, 1, True)

                # 再找一次轮廓
                cnts = cv.findContours(ellipseImg, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
                cnts = imutils.grab_contours(cnts)
                cntsSorted = sorted(cnts, key=cv.contourArea, reverse=True)
                rectBox = cv.minAreaRect(cntsSorted[0])
                rectBoxPoints = np.int0(cv.boxPoints(rectBox))
                useImg = boardColor.copy()
                cv.drawContours(useImg, [rectBoxPoints], -1, (0, 255, 0), 3)
                image_show("InnerBoardFinded", useImg, 0, True)

            res = check_board_size(cntsSorted[0], width, height, calcThresBig, calcThresSmall)
            return res
        else:
            res["result"] = "error"
            res["info"] = "识别的面积过大"
            res["calcSize"] = (imgMoveAngle.shape[1] // MM2Pix, imgMoveAngle.shape[0] // MM2Pix)
            res["calcError"] = abs(imgMoveAngle.shape[1] - w) // MM2Pix, abs(imgMoveAngle.shape[0] - h) // MM2Pix
            return res



    except Exception as e:
        res["result"] = "error"
        res["info"] = str(e)
        return res


def get_inner_board_info(board, jointLine, width, height, calcThresBig, calcThresSmall, boardPos):
    """
    获取内板的识别信息
    :param board: 传过来的包括一个内板的原始图
    :param jointLine: 传过来的包括一个内板的原始图的拼接处
    :param width: 内板宽
    :param height: 内板高
    :param calcThresBig:偏大阈值
    :param calcThresSmall: 偏小阈值
    :param boardPos: 板的位置,例如两拼板,就是上和下
    :return: 是否识别成功,识别的信息,框的顶点坐标,识别的内板尺寸(w,h),识别的尺寸误差(w_error,h_error)
    """
    # 参数说明,第一个是高斯滤波的核大小,0,表示不进行高斯滤波.第二个是求方差滤波的时候使用核大小,
    # 第三个参数是膨胀腐蚀的核大小,第四个参数是,膨胀腐蚀次数,第五个参数是腐蚀膨胀次数,第六个参数是找内板所用的阈值.如果是0,就表示使用大律法
    calcArgs = {"1": [2, 8, 9, 5, 8, 120, 0.5],
                "2": [2, 8, 9, 5, 8, 30, 0.2],
                "3": [10, 11, 11, 8, 10, 10, 0.5]}
    sizeDifRes = []  # 保存尺寸异常的情况
    rectWidth = max(int(width * 0.3), int(150 * MM2Pix))
    rectHeight = int(200 * MM2Pix)
    boardColor = cv.cvtColor(board, cv.COLOR_GRAY2BGR)

    for tryTimes in range(1, len(calcArgs.keys()) + 1):
        image_destroy()
        gaussianRadius, squareRadius, dilateKernel, dilateTimes, erodeTimes, thresOffset, scharrRate = calcArgs.get(str(tryTimes))
        print("算法进行第{}次计算,计算参数:(高斯半径:{}),(方差核:{}),(膨胀腐蚀核:{}),(膨胀腐蚀次数:{}),(腐蚀膨胀次数:{})),(阈值offset:{}),(scharr比例:{})".format(
            tryTimes, gaussianRadius, squareRadius, dilateKernel, dilateTimes, erodeTimes, thresOffset, scharrRate))
        if gaussianRadius != 0:
            blurred = cv.GaussianBlur(board, (0, 0), gaussianRadius)
        else:
            blurred = board.copy()
        scharrX = cv.Scharr(blurred, cv.CV_64F, 1, 0)
        scharrX = cv.convertScaleAbs(scharrX)
        scharrY = cv.Scharr(blurred, cv.CV_64F, 0, 1)
        scharrY = cv.convertScaleAbs(scharrY)
        scharrXY = cv.addWeighted(scharrX, scharrRate, scharrY, scharrRate, 0)
        image_show("ScharrXY", scharrXY, 0, True)

        # 求均方差
        img32 = np.float32(scharrXY)
        square1 = cv.blur(np.multiply(img32, img32), (squareRadius, squareRadius))
        square2 = np.multiply(cv.blur(img32, (squareRadius, squareRadius)), cv.blur(img32, (squareRadius, squareRadius)))
        squareDif32 = np.subtract(square1, square2)
        squareDif = squareDif32.copy()
        np.clip(squareDif32, 0, 255, out=squareDif)
        squareDif = squareDif.astype("uint8")
        image_show("SquareDif", squareDif, 1, True)

        # 1.进行膨胀腐蚀,分割图像
        contourImg = squareDif.copy()
        kernelUsed = cv.getStructuringElement(cv.MORPH_RECT, (dilateKernel, dilateKernel))
        contourImg = cv.dilate(contourImg, kernelUsed, iterations=dilateTimes)
        contourImg = cv.erode(contourImg, kernelUsed, iterations=dilateTimes)
        image_show("squareDifDilateErode", contourImg, 0, True)

        # 2.根据阈值进行分割查找
        thresUsed = contourImg[rectHeight:int(contourImg.shape[0] - rectHeight), rectWidth:contourImg.shape[1] - rectWidth]
        print("使用阈值 np.mean(thresUsed) = {},thresOffset = {}".format(np.mean(thresUsed), thresOffset))
        _, contourImg = cv.threshold(contourImg, np.mean(thresUsed) + thresOffset, 255, cv.THRESH_BINARY)
        # 3.处理拼接处,并进行泛红填充
        contourImg = cv.subtract(contourImg, jointLine)

        mask = np.zeros((contourImg.shape[0] + 2, contourImg.shape[1] + 2), dtype=np.uint8)
        cv.floodFill(contourImg, mask, (contourImg.shape[1] // 2, contourImg.shape[0] // 2), 150)
        floodFill = np.zeros(contourImg.shape, dtype=np.uint8)
        floodFill[contourImg == 150] = 255
        image_show("FloodFill", floodFill, 0, True)

        # 4.进行腐蚀膨胀,去除边界的噪声
        floodFill = cv.erode(floodFill, kernelUsed, iterations=erodeTimes)
        floodFill = cv.dilate(floodFill, kernelUsed, iterations=erodeTimes)
        image_show("FloodFillErodeDilate", floodFill, 0, True)

        # 去掉两个角,要分是上框还是下框. 不去除中缝的角,只去除1,3刀的角
        if boardPos == "up":
            cv.rectangle(floodFill, (0, 0), (rectWidth, rectHeight), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1], 0), (floodFill.shape[1] - rectWidth, rectHeight), 0, -1)
        elif boardPos == "down":
            cv.rectangle(floodFill, (0, floodFill.shape[0]), (rectWidth, floodFill.shape[0] - rectHeight), 0, -1)
            cv.rectangle(floodFill, (floodFill.shape[1], floodFill.shape[0]), (floodFill.shape[1] - rectWidth, floodFill.shape[0] - rectHeight),
                         0, -1)
        image_show("MoveAngle", floodFill, 0, True)

        res = check_inner_board(floodFill, width, height, calcThresBig, calcThresSmall, boardColor, boardPos)
        print("第{}次识别结果:{},识别信息:{},识别尺寸:{},识别误差:{}".format(tryTimes, res["result"], res["info"], res["calcSize"], res["calcError"]))
        if res["result"] == "ok":
            return "ok", res["info"], res["box"], res["calcSize"], res["calcError"]
        elif res["result"] != "error":
            sizeDifRes.append(res)
            continue
    else:
        # 如果没有成功返回,找到
        if len(sizeDifRes) >= 1:
            # 按照误差去排序,返回一个结果即可.
            resSorted = sorted(sizeDifRes, key=lambda x: np.sum(x["calcError"]))
            res = resSorted[0]
            return "sizeDif", res["info"], res["box"], res["calcSize"], res["calcError"]
        else:
            return "error", res["info"], res["box"], res["calcSize"], res["calcError"]


def get_two_boxes(image, width, height, calcThresBig, calcThresSmall, noCut):
    """
    获取图像的两个内板框的顶点坐标.在物料区域上的顶点坐标
    :param image: 图像的灰度图
    :param width: 内板尺寸宽度,单位像素
    :param height: 内板尺寸高度,单位像素
    :param calcThresBig: 识别的结果的偏大阈值,单位mm
    :param calcThresSmall: 识别的结果的偏小阈值,单位mm
    :param noCut: 是否四边都不切
    :return: 返回值,是否识别成功,识别的信息,两个框的顶点坐标,识别的内板尺寸,[(上框_宽,上框_高),(下框_宽,下框_高),识别的尺寸误差[(上框_w_error,上框_h_error),
            (下框_w_误差,下框_高误差)]],totalBoard,整个物料框
    """
    if image is None:
        return False, "图像异常,请重拍", None, None, None, None, None
    if len(image.shape) == 3:
        image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    # 分成如下几步去做
    # 1> 首先是寻找物料区域
    # 使用台布的均值+15,作为物料区域寻找的阈值分割,并且把找到的轮廓小于0.1倍面积的轮廓过滤掉.
    totalBoardThresUsed = np.mean(image[:150, :1100]) + 15
    filterAreaRate = 0.1
    imageGray, totalBoardBox = find_total_board(image, width, height, totalBoardThresUsed, filterAreaRate)
    (cx, cy), (w, h), angle = totalBoardBox
    widthHalf = int(w / 2)
    heightHalf = int(h / 2)
    yStart = max(int(cy - heightHalf), 0)  # 之所以有零的情况是因为旋转之后物料框可能跑外面去了.
    yEnd = min(int(cy + heightHalf), image.shape[0])  # y也是一样的,旋转之后后面图像出去了.这个时候结束位置就是整个图像的部位
    xStart = max(int(cx - widthHalf), 0)
    xEnd = min(int(cx + widthHalf), image.shape[1])
    M = cv.getRotationMatrix2D((cx, cy), angle, 1)
    totalBoard = cv.warpAffine(imageGray, M, (image.shape[1], image.shape[0]))[yStart:yEnd, xStart:xEnd]
    totalBoardColor = cv.cvtColor(totalBoard, cv.COLOR_GRAY2BGR)
    jointLineMask = np.zeros(image.shape, dtype=np.uint8)
    jointLineMask[:, 1106 - 15: 1106 + 15] = 255  # 处理拼接处
    jointLineMaskRotated = cv.warpAffine(jointLineMask, M, (image.shape[1], image.shape[0]))[yStart:yEnd, xStart:xEnd]
    image_show("TotalBoard", totalBoard, 0, True)

    # 2> 判断找到的物料区域是否满足要求.物料区域的高度不能太高.宽度也不能太宽.如果大于一定的值,或者小于一定的值.就证明找错了.这个值要在settings里面设定
    if h > int(TOTAL_BOARD_HEIGHT * MM2Pix):
        return False, "寻找到的物料框的高度是:{},超过物料框高度阈值:{}".format(h // MM2Pix, TOTAL_BOARD_HEIGHT), None, None, None, None, totalBoard

    # 3> 根据是几拼板进行分框处理
    if jointNumber == "2":
        upDownSlice = int(20 * MM2Pix)  # 将图像上下截断,20个mm.上框从中点向下延长20个mm,下框从中点向上延长20个mm
        upBoard = totalBoard.copy()[:int(heightHalf + upDownSlice), :]
        downBoard = totalBoard.copy()[int(heightHalf - upDownSlice):, :]
        upJointLine = jointLineMaskRotated.copy()[:int(heightHalf + upDownSlice), :]
        downJointLine = jointLineMaskRotated.copy()[int(heightHalf - upDownSlice):, :]

        # 根据这两个框分别去求它的信息
        upRes, upInfo, upBox, upCalcSize, upError = get_inner_board_info(upBoard, upJointLine, width, height, calcThresBig, calcThresSmall, "up")
        downRes, downInfo, downBox, downCalcSize, downError = get_inner_board_info(downBoard, downJointLine, width, height, calcThresBig,
                                                                                   calcThresSmall, "down")
        if upRes == "ok" and downRes == "ok":
            downBoxPoints = [(p[0], p[1] + heightHalf - upDownSlice) for p in downBox]
            cv.drawContours(totalBoardColor, [np.array(upBox), np.array(downBoxPoints)], -1, (0, 255, 0), 2)
            image_show("TwoBoxes", totalBoardColor, 0, True)
            return "ok", upInfo + downInfo, upBox, downBox, (upCalcSize, downCalcSize), (upError, downError), totalBoard
        elif upRes == "sizeDif" or downRes == "sizeDif":
            return "sizeDif", upInfo + downInfo, upBox, downBox, (upCalcSize, downCalcSize), (upError, downError), totalBoard
        else:
            return "error", upInfo + downInfo, upBox, downBox, (upCalcSize, downCalcSize), (upError, downError), totalBoard



if __name__ == '__main__':
    filePath = r"D:\chongda_raw\chongda_new_pic\fail "
    keepTestPassedPath = r"D:\chongda_raw\raw_ok"
    MM2Pix = 3.2038
    mtx = [[0, 1, 0], [1, 0, 0]]  # 模拟一个矩阵
    calcThresBig = 15  # 偏大阈值
    calcThresSmall = 10  # 偏小阈值
    remainEdge = [1, 1, 1, 1]  # 留边宽度
    jointNumber = "2"  # 几拼板
    topRemain, leftRemain, bottomRemain, rightRemain = [int(x * MM2Pix) for x in remainEdge]
    noCut = False if any(remainEdge) else True  # 是否四边都不切的情况
    nowTime = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())
    keepDir = filePath

    if os.path.isfile(filePath):
        keepDir, _ = os.path.split(filePath)

    # 保存成功结果的实际路径
    # 保存失败图片的实际路径
    # 成功的图就删除掉,另外保存起来.放到图库里.
    keepResPath = os.path.join(keepTestPassedPath, "cutlines")
    keepFailPath = os.path.join(keepDir, "fail")

    if not os.path.exists(keepTestPassedPath):
        os.mkdir(keepTestPassedPath)
    if not os.path.exists(keepResPath):
        os.mkdir(keepResPath)
    if not os.path.exists(keepFailPath):
        os.mkdir(keepFailPath)

    fileNames = []  # 保存要去识别的所有的板
    if os.path.isfile(filePath):
        filePath, fileName = os.path.split(filePath)
        fileNames.append(fileName)
    else:
        for root, dirs, files in os.walk(filePath):
            if root != filePath:  # 过滤掉不是要处理的图像的目录
                continue
            for file in files:
                fileNames.append(file)

    for index, fileName in enumerate(fileNames):
        filePathReal = os.path.join(filePath, fileName)
        widthTest, heightTest = get_width_and_height(fileName)
        if widthTest is None or heightTest is None:
            print("*" * 10 + " 第{} 张图 ({}) 图像名称错误,已经跳过".format(index + 1, fileName) + "*" * 10)
        print("*" * 10 + " 第{} 张图 ({})".format(index + 1, fileName) + "*" * 10)

        imgTest = cv.imread(filePathReal, cv.IMREAD_GRAYSCALE)
        totalBoardTest = None

        # 开始调用算法开始时间
        calcStart = time.time()
        if jointNumber == "2":
            calcRes, calcInfo, upBox, downBox, boardSize, boardError, totalBoardTest = get_two_boxes(imgTest, widthTest, heightTest, calcThresBig,
                                                                                                     calcThresSmall, noCut)
            if calcRes == "ok":
                print("识别成功")
                cv.imwrite(os.path.join(keepTestPassedPath, fileName), imgTest)
                os.remove(filePathReal)  # 然后移除掉,免得算法来回的替换
                continue
            elif calcRes == "sizeDif":
                print("识别尺寸错误" + calcInfo)
                cv.imwrite(os.path.join(keepFailPath, fileName), imgTest)
                os.remove(filePathReal)
                continue
            else:
                print("识别出错" + calcInfo)
                cv.imwrite(os.path.join(keepFailPath, fileName), imgTest)
                os.remove(filePathReal)
                continue
